//
// Created by twetec on 25-10-15.
//

#include <gtest/gtest.h>
#include <asio2/udp/udp_client.hpp>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <chrono>
#include <thread>

#include <network_prober/probe_detect/handle_udp_probe_detect.h>

namespace tests::probe_detect {
namespace
{
TEST(HandleUDPProbeDetectIPv4Test, testAsio2UDP)
{
    asio2::udp_client client;

    // 连接的超时时间
    // client.set_connect_timeout(std::chrono::seconds(10));
    // 断开重连
    client.set_auto_reconnect(false);

    // 在socket初始化时调用
    client.bind_init([&]() {
        // 绑定请求从哪个网口发送
        asio::ip::udp::endpoint endpoint(asio::ip::address_v4::from_string("192.168.0.108"), 0);
        client.socket().bind(endpoint);
    });

    client.bind_connect([&]() {
        std::cout << "连接了吗?" << std::endl;
    });

    client.bind_handshake([&]() {
        std::cout << "握手咯" << std::endl;
    });

    client.bind_recv([&](std::string_view data) {
        std::cout << "收到消息: " << data << std::endl;
    });

    client.bind_disconnect([&]() {
        std::cout << "断开了吗?" << std::endl;
    });

    client.start("192.168.0.104", 8080, asio2::use_dgram);

    std::cout << "连接上了吧" << std::endl;

    client.async_send("PING");

    while (std::getchar() != '\n');
}

TEST(HandleUDPProbeDetectIPv4Test, testDetect)
{
    // 创建探测指令对象
    auto probeDetectInstructionPtr = std::make_shared<network_prober::probe_detect::HandleUDPProbeDetectIPv4>("192.168.0.108", "192.168.0.104", 8080);

    // 监听指令执行结果(延迟了对象生命周期)
    probeDetectInstructionPtr->setOnDetectedCallback([](network_prober::probe_detect::UDPProbeDetectResultIPv4&& result) {
        std::cout << result << std::endl;
    });

    // 执行指令
    probeDetectInstructionPtr->detect();

    while (std::getchar() != '\n');
}

TEST(HandleUDPProbeDetectIPv4Test, testPosix)
{
    // 创建UDP套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        std::cerr << "Socket creation failed" << std::endl;
        FAIL();
    }

    // 设置接收超时
    struct timeval tv;
    tv.tv_sec = 5000 / 1000;
    tv.tv_usec = (5000 % 1000) * 1000;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    // // 绑定本机地址
    // struct sockaddr_in clientAddr;
    // memset(&clientAddr, 0, sizeof(clientAddr));
    // clientAddr.sin_family = AF_INET;
    // clientAddr.sin_port = htons(0);
    // if (inet_pton(AF_INET, "192.168.0.108", &clientAddr.sin_addr) <= 0) {
    //     std::cerr << "Invalid address" << std::endl;
    //     close(sockfd);
    //     FAIL();
    // }
    // // 将监听的本地IP和端口与文件描述符绑定
    // if (bind(sockfd, (sockaddr *)&clientAddr, sizeof(clientAddr)) < 0) {
    //     std::cerr << "Invalid interface" << std::endl;
    //     close(sockfd);
    //     FAIL();
    // }

    std::string device_name{"wlxe0e1a9384141nnn"};
    std::cout << sizeof("wlxe0e1a9384141nnn") << std::endl;
    std::cout << device_name.size() << std::endl;
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "wlxe0e1a9384141JJJJJ", sizeof("wlxe0e1a9384141JJJJJ")) == -1) {
        std::cerr << "Failed to bind to device: " << strerror(errno) << std::endl;
    }

    // 准备服务器地址
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8080);
    if (inet_pton(AF_INET, "192.168.0.104", &servaddr.sin_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 发送探测数据包
    const char* message = "UDP_PROBE222";
    if (sendto(sockfd, message, strlen(message), 0, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        std::cerr << "Send failed" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 尝试接收响应
    char buffer[1024];
    socklen_t len = sizeof(servaddr);
    ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&servaddr, &len);

    close(sockfd);

    std::cout << "收到: " << n << " 个字节" << std::endl;
    std::cout << "消息：" << std::string{buffer} << std::endl;
}

TEST(HandleUDPProbeDetectIPv6Test, testAsio2UDP)
{
    asio2::udp_client client;

    // 连接的超时时间
    // client.set_connect_timeout(std::chrono::seconds(10));
    // 断开重连
    client.set_auto_reconnect(false);

    // // 在socket初始化时调用
    // client.bind_init([&]() {
    //     // 绑定请求从哪个网口发送
    //     asio::ip::udp::endpoint endpoint(asio::ip::address_v6::from_string("fd00:7860:5bfc:44d0:1de9:be6a:9f90:7f83"), 0);
    //     client.socket().bind(endpoint);
    // });

    client.bind_connect([&]() {
        std::cout << "连接了吗?" << std::endl;
    });

    // client.bind_handshake([&]() {
    //     std::cout << "握手咯" << std::endl;
    // });

    client.bind_recv([&](std::string_view data) {
        std::cout << "收到消息: " << data << std::endl;
    });

    client.bind_disconnect([&]() {
        std::cout << "断开了吗?" << std::endl;
    });

    client.start("fd00:7860:5bfc:44d0::1000%wlxe0e1a9384141", 8080, asio2::use_dgram);

    std::cout << "连接上了吧" << std::endl;

    client.async_send("PINGIPv6");

    while (std::getchar() != '\n');
}

TEST(HandleUDPProbeDetectIPv6Test, testDetect)
{
    // 创建探测指令对象
    auto probeDetectInstructionPtr = std::make_shared<network_prober::probe_detect::HandleUDPProbeDetectIPv6>("fd00:7860:5bfc:44d0:1de9:be6a:9f90:7f83", "fd00:7860:5bfc:44d0::1001", 8081);

    // 监听指令执行结果(延迟了对象生命周期)
    probeDetectInstructionPtr->setOnDetectedCallback([](network_prober::probe_detect::UDPProbeDetectResultIPv6&& result) {
        std::cout << result << std::endl;
    });

    // 执行指令
    probeDetectInstructionPtr->detect();

    while (std::getchar() != '\n');
}

TEST(HandleUDPProbeDetectIPv6Test, testPosix)
{
    // 创建UDP套接字
    int sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        std::cerr << "Socket creation failed" << std::endl;
        FAIL();
    }

    // 设置接收超时
    struct timeval tv;
    tv.tv_sec = 5000 / 1000;
    tv.tv_usec = (5000 % 1000) * 1000;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    // const char *device_name = "fd00:7860:5bfc:44d0:9f00:cea8:5398:bcee";
    // std::cout << sizeof(device_name) << std::endl;
    // std::cout << strlen(device_name) << std::endl;
    // if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, device_name, strlen(device_name)+1) == -1) {
    //     std::cerr << "Failed to bind to device: " << strerror(errno) << std::endl;
    // }

    // 绑定本机地址
    struct sockaddr_in6 clientAddr;
    memset(&clientAddr, 0, sizeof(clientAddr));
    clientAddr.sin6_family = AF_INET6;
    clientAddr.sin6_port = htons(0);
    if (inet_pton(AF_INET6, "fe80::e974:78cb:ae7:219d", &clientAddr.sin6_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 将监听的本地IP和端口与文件描述符绑定
    if (bind(sockfd, (sockaddr *)&clientAddr, sizeof(clientAddr)) < 0) {
        std::cerr << "Invalid interface: " << strerror(errno) << std::endl;
        close(sockfd);
        FAIL();
    }

    // 准备服务器地址
    struct sockaddr_in6 servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin6_family = AF_INET6;
    servaddr.sin6_port = htons(8085);
    if (inet_pton(AF_INET6, "fd00:7860:5bfc:44d0::1004", &servaddr.sin6_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 发送探测数据包
    const char* message = "UDP_PROBE111";
    if (sendto(sockfd, message, strlen(message), 0, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        std::cerr << "Send failed" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 尝试接收响应
    char buffer[1024];
    socklen_t len = sizeof(servaddr);
    ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&servaddr, &len);

    close(sockfd);

    std::cout << "收到: " << n << " 个字节" << std::endl;
    std::cout << "消息：" << std::string{buffer} << std::endl;
}

}}
